package cn.wolfcode.mq.listener;

import cn.wolfcode.domain.RefundLog;
import cn.wolfcode.mapper.RefundLogMapper;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.service.IOrderInfoService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

//2.收到MQ发送的事务消息，执行本地事务
@Slf4j
@Component
@RocketMQTransactionListener(txProducerGroup = MQConstant.INTERGRAL_REFUND_TX_PRODUCT_GROUP)
public class IntergralRefundTxMessageListener implements RocketMQLocalTransactionListener {

    private final IOrderInfoService orderInfoService;
    private final RefundLogMapper refundLogMapper;

    public IntergralRefundTxMessageListener(IOrderInfoService orderInfoService, RefundLogMapper refundLogMapper) {
        this.orderInfoService = orderInfoService;
        this.refundLogMapper = refundLogMapper;
    }

    //2.1 执行本地事务
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        log.info("[事务消息监听器] 准备执行本地事务，进行积分退款 message = {} , arg = {}", JSON.toJSONString(msg), JSON.toJSONString(arg));
        try {
            //修订订单状态，库存回补，删除用户下单标识，删除本地标识，记录退款日志
            orderInfoService.integralRefundRollback((String) arg);
            //如果执行回滚消息成功，进行消息 UNKNOWN 提交，则会执行回查本地事务的状态(checkLocalTransaction)，直到执行 COMMIT 提交，通知到消费者
            return RocketMQLocalTransactionState.UNKNOWN;
        } catch (Exception e) {
            log.warn("[事务消息监听器] 本地事务执行异常", e);
            //如果执行回顾消息失败，则进行 ROLLBACK 回滚
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }

    //2.2 回查本地事务的状态
    //当执行本地事务返回的是 UNKNOWN状态，则执行该回查本地事务的状态的方法
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        //msg.getPayload() 由于拿到的值是二进制，因此需要通过 msg.getHeaders().get() 拿到 orderNo
        log.info("[事务消息监听器] 本地事务执行完成未获得结果，准备检查订单状态是否已退款: payload={} , header={}", JSON.toJSONString(msg.getPayload()), JSON.toJSONString(msg.getHeaders().get("outTradeNo")));
        //基于 orderNo 查询积分退款流水,如果已有退款记录，说明退款成功，否则退款失败
        String outTradeNo = String.valueOf(msg.getHeaders().get("outTradeNo"));
        RefundLog refundLog = refundLogMapper.findByOutTradeNo(outTradeNo);
        if (refundLog != null){
            //表示积分退款成功，则进行 COMMIT 提交消息，通知消息给消费者
            return RocketMQLocalTransactionState.COMMIT;
        }
        //如果积分退款失败，则进行 UNKNOWN 消息，再次回查本地事务
        return RocketMQLocalTransactionState.UNKNOWN;
    }
}
